﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using GraphTest.Geometry2D.Interfaces;

namespace GraphTest.Geometry2D
{
    public class Segment2D
    {
        public Segment2D()
        {
            P1 = new Point2D();
            P2 = new Point2D();
        }

        public Segment2D(IPoint2D p1, IPoint2D p2)
        {
            P1 = new Point2D(p1.X, p1.Y);
            P2 = new Point2D(p2.X, p2.Y);
        }

        public Segment2D(Point2D p1, Point2D p2)
            : this((IPoint2D)p1, (IPoint2D)p2)
        { }

        public Segment2D(Vector2D v1, Vector2D v2)
            : this((IPoint2D)v1, (IPoint2D)v2)
        { }

        
        
        public Point2D P1 { get; set; }
        public Point2D P2 { get; set; }



        public Vector2D GetVector()
        {
            return new Vector2D(P1, P2);
        }


        public bool IsSegmentIntersected(Segment2D s)
        {
            return IsSegmentsIntersected(s, this);
        }

        public static bool IsSegmentsIntersected(Segment2D s1, Segment2D s2)
        {
            return IsSegmentsIntersected(s1.P1.X, s1.P1.Y, s1.P2.X, s1.P2.Y, s2.P1.X, s2.P1.Y, s2.P2.X, s2.P2.Y);
        }

        protected static bool IsSegmentsIntersected(
            float x1, float y1,
            float x2, float y2,
            float x3, float y3,
            float x4, float y4)
        {
            bool result = false;

            float Ex = x2 - x1;
            float Ey = y2 - y1;

            float Fx = x4 - x3;
            float Fy = y4 - y3;

            float Qx = x1 - x3;
            float Qy = y1 - y3;

            float D = Ex * Fy - Ey * Fx;

            if (D == 0)
            {
                result = false;
            }
            else
            {
                float U1 = Fx * Qy - Fy * Qx;
                float U2 = Ex * Qy - Ey * Qx;

                float h1 = U1 / D;
                float h2 = U2 / D;

                result = (0.0 <= h1 && h1 <= 1.0) && (0.0 <= h2 && h2 <= 1.0);
            }

            return result;
        }

        public Point2D GetIntersectionPoint(Segment2D s)
        {
            return GetIntersectionPoint(s, this);
        }
        
        public static Point2D GetIntersectionPoint(Segment2D s1, Segment2D s2)
        {
            Point2D result = null;

            if (!IsSegmentsIntersected(s1, s2))
                return result;

            float x1 = s1.P1.X;
            float y1 = s1.P1.Y;
            float x2 = s1.P2.X;
            float y2 = s1.P2.Y;
            float x3 = s2.P1.X;
            float y3 = s2.P1.Y;
            float x4 = s2.P2.X;
            float y4 = s2.P2.Y;

            float R = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
            float D1 = x1 * y2 - x2 * y1;
            float D2 = x3 * y4 - x4 * y3;
            float Rx = D1 * (x3 - x4) - D2 * (x1 - x2);
            float Ry = D1 * (y3 - y4) - D2 * (y1 - y2);

            float x0 = Rx / R;
            float y0 = Ry / R;

            result = new Point2D(x0, y0);

            return result;
        }

        public static bool? IsNormalSegmentsIntersected(Segment2D s1, Segment2D s2)
        {
            bool result = false;

            if (s1.P1.X == s1.P2.X && s1.P1.Y == s1.P2.Y)
                return null;

            if (s2.P1.X == s2.P2.X && s2.P1.Y == s2.P2.Y)
                return null;

            Segment2D sv = null;
            Segment2D sh = null;

            if (s1.P1.X == s1.P2.X)
                sh = s1;
            else if (s1.P1.Y == s1.P2.Y)
                sv = s1;

            if (s2.P1.X == s2.P2.X)
                sh = s2;
            else if (s2.P1.Y == s2.P2.Y)
                sv = s2;

            if (sh == null || sv == null)
                return null;

            result = Math.Min(sh.P1.X, sh.P2.X) <= sv.P1.X
                    && sv.P1.X <= Math.Max(sh.P1.X, sh.P2.X)
                    && Math.Min(sv.P1.Y, sv.P2.Y) <= sh.P1.Y
                    && sh.P1.Y <= Math.Max(sv.P1.Y, sv.P2.Y);

            return result;
        }
    }
}
